<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="utf-8">
    <meta name="viewport" content="width=500">
    <title>Usermod Settings</title>
    <script>
    var d = document;
    var umCfg = {};
    var pins = [6,7,8,9,10,11];
    var pinO = ["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"], owner;
    var loc = false, locip;
    var urows;
    var numM = 0;
    function gId(s) { return d.getElementById(s); }
    function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
    function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
    function B() { window.open("/settings","_self"); }
    function S() {
        if (window.location.protocol == "file:") {
            loc = true;
            locip = localStorage.getItem('locIp');
            if (!locip) {
                locip = prompt("File Mode. Please enter WLED IP!");
                localStorage.setItem('locIp', locip);
            }
        }
        GetV();
        if (numM > 0 || locip) ldS();
        else gId("um").innerHTML = "No Usermods installed.";
    }
    // https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
    function isF(n) { return n === +n && n !== (n|0); }
    function isI(n) { return n === +n && n === (n|0); }
    function check(o,k) {   // input object, pin owner key
        var n = o.name.replace("[]","").substr(-3);
        if (o.type=="number" && n.substr(0,3)=="pin") {
            for (var i=0; i<pins.length; i++) {
                if (k==pinO[i]) continue;
                if (o.value==pins[i] || o.value<-1 || o.value>39) { o.style.color="red"; break; } else o.style.color=o.value>33?"orange":"#fff";
            }
        }
    }
    function getPins(o) {
        if (isO(o)) {
            for (const [k,v] of Object.entries(o)) {
                if (isO(v)) {
                    owner = k;
                    getPins(v);
                    continue;
                }
                if (k.replace("[]","").substr(-3)=="pin") {
                    if (Array.isArray(v)) {
                        for (var i=0; i<v.length; i++) if (v[i]>=0) { pins.push(v[i]); pinO.push(owner); }
                    } else {
                        if (v>=0) { pins.push(v); pinO.push(owner); }
                    }
                } else if (Array.isArray(v)) {
                    for (var i=0; i<v.length; i++) getPins(v[i]);
                }
            }
        }
    }
    function addField(k,f,o,a=false) {  //key, field, (sub)object, isArray
        if (isO(o)) {
            for (const [s,v] of Object.entries(o)) {
                // possibility to nest objects (only 1 level)
                if (f!=='unknown' && !k.includes(":")) addField(k+":"+f,s,v);
                else addField(k,s,v);
            }
        } else if (Array.isArray(o)) {
            for (var j=0; j<o.length; j++) {
                addField(k,f,o[j],true);
            }
        } else {
            var c, t = typeof o;
            switch (t) {
                case "boolean":
                    t = "checkbox"; c = 'value="true"' + (o ? ' checked' : '');
                    break;
                case "number":
                    c = `value="${o}"`;
                    if (f.substr(-3)==="pin") {
                        c += ' max="39" min="-1" style="width:40px;"';
                        t = "int";
                    } else {
                        c += ' step="any" style="width:100px;"';
                    }
                    break;
                default:
                    t = "text"; c = `value="${o}" style="width:250px;"`;
                    break;
            }
            if (k.includes(":")) urows += k.substr(k.indexOf(":")+1);
            urows += ` ${f}: `;
            // https://stackoverflow.com/questions/11657123/posting-both-checked-and-unchecked-checkboxes
            if (t=="checkbox") urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="false">`;
            else if (!a)       urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="${t}">`;
            urows += `<input type="${t==="int"?"number":t}" name="${k}:${f}${a?"[]":""}" ${c} oninput="check(this,'${k.substr(k.indexOf(":")+1)}')"><br>`;
        }
    }
    function ldS() {
        var url = (loc?`http://${locip}`:'') + '/cfg.json';
        fetch(url, {
            method: 'get'
        })
        .then(res => {
            if (!res.ok) gId('lserr').style.display = "inline";
            return res.json();
        })
        .then(json => {
            umCfg = json.um;
            getPins(json);
            urows="";
            if (isO(umCfg)) {
                for (const [k,o] of Object.entries(umCfg)) {
                    urows += `<hr><h3>${k}</h3>`;
                    addField(k,'unknown',o);
                }
            }
            if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
            gId("um").innerHTML = urows;
        })
        .catch(function (error) {
            gId('lserr').style.display = "inline"
            console.log(error);
        });
    }
    function svS(e) {
        e.preventDefault();
        console.log(d.Sf);
        if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
    }
    function GetV() {}
    </script>
    <style>
        @import url("style.css");
    </style>
</head>

<body onload="S()">
	<form id="form_s" name="Sf" method="post" onsubmit="svS(event)">
		<div class="toprow">
		<div class="helpB"><button type="button" onclick="H()">?</button></div>
		<button type="button" onclick="B()">Back</button><button type="submit">Save</button><br>
		<span id="lssuc" style="color:green; display:none">&#10004; Configuration saved!</span>
		<span id="lserr" style="color:red; display:none">&#9888; Could not load configuration.</span><hr>
		</div>
		<h2>Usermod Setup</h2>
        <div id="um">Loading settings...</div>
		<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
	</form>
</body>

</html>